
#include "../Header/cBloom.h"
#include "../Header/ShaderObject.h"
#include "../Header/framebufferObject.h"
#include "../Header/renderbuffer.h"
#include "../Header/glErrorUtil.h"
#include "../Header/misc.h"

cBloom::cBloom(int _w, int _h, unsigned int textureTarget)
{
	texTarget = textureTarget;
	w = _w;
	h = _h;
	wDown = (int)(w * 0.25);
	hDown = (int)(h * 0.25);

	rb = new Renderbuffer(GL_DEPTH_COMPONENT24, w, h);
	sceneFBO = new FramebufferObject();
	CheckErrorsGL("cBloom::cBloom sceneFBO  BEGIN : Creating textures");
	sceneFBO->Bind();
		glGenTextures(1, &sceneTexId);
		glBindTexture (texTarget, sceneTexId);
		glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);			
		glTexImage2D(texTarget, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_FLOAT, 0);			

		glGenTextures(1, &finalCompTexId);
		glBindTexture (texTarget, finalCompTexId);
		glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);			
		glTexImage2D(texTarget, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_FLOAT, 0);		

		CheckErrorsGL("cBloom::cBloom sceneFBO  END : Creating textures");
		CheckErrorsGL("cBloom::cBloom BEGIN : Configuring FBO");
		sceneFBO->AttachTexture(texTarget, sceneTexId, GL_COLOR_ATTACHMENT0_EXT);
		sceneFBO->AttachTexture(texTarget, finalCompTexId, GL_COLOR_ATTACHMENT1_EXT);
		sceneFBO->AttachRenderBuffer(rb->GetId(), GL_DEPTH_ATTACHMENT_EXT);
		sceneFBO->IsValid();
	sceneFBO->Disable();
	CheckErrorsGL("cBloom::cBloom END : Configuring FBO");

	downSampleFBO = new FramebufferObject();
	CheckErrorsGL("cBloom::cBloom downSampleFBO  BEGIN : Creating textures");
	downSampleFBO->Bind();
		glGenTextures(1, &downSampleTexId);
		glBindTexture (texTarget, downSampleTexId);
		glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);			
		glTexImage2D(texTarget, 0, GL_RGBA8, wDown, hDown, 0, GL_RGBA, GL_FLOAT, 0);

		glGenTextures(1, &blurHTexId);
		glBindTexture (texTarget, blurHTexId);
		glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);			
		glTexImage2D(texTarget, 0, GL_RGBA8, wDown, hDown, 0, GL_RGBA, GL_FLOAT, 0);	

		glGenTextures(1, &finalBlurTexId);
		glBindTexture (texTarget, finalBlurTexId);
		glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);			
		glTexImage2D(texTarget, 0, GL_RGBA8, wDown, hDown, 0, GL_RGBA, GL_FLOAT, 0);	


		CheckErrorsGL("cBloom::cBloom downSampleFBO  END : Creating textures");
		CheckErrorsGL("cBloom::cBloom downSampleFBO BEGIN : Configuring FBO");
		downSampleFBO->AttachTexture(texTarget, downSampleTexId, GL_COLOR_ATTACHMENT0_EXT);
		downSampleFBO->AttachTexture(texTarget, blurHTexId, GL_COLOR_ATTACHMENT1_EXT);
		downSampleFBO->AttachTexture(texTarget, finalBlurTexId, GL_COLOR_ATTACHMENT2_EXT);
		downSampleFBO->IsValid();
	downSampleFBO->Disable();
	CheckErrorsGL("cBloom::cBloom downSampleFBO END : Configuring FBO");

	displayTexRect = new ShaderObject("shaders/passthruTexRectFragment.glsl");
	displayTexRect->activate();
		displayTexRect->setUniformi("tex",0);
	displayTexRect->deactivate();
	displayTex2D =0;

	downSampleShader = new ShaderObject("shaders/bloom/downSampleFragment.glsl");
	downSampleShader->activate();
		downSampleShader->setUniformi("scene",0);
		downSampleShader->setUniformf("HighlightThreshold", 0.7f);	
	downSampleShader->deactivate();

	float winSize[2];
	winSize[0] = (float)wDown;
	winSize[1] = (float)hDown;
	float weight[] = {0.05f, 0.1f, 0.2f, 0.3f, 0.2f, 0.1f, 0.05f}; 
	blur = new ShaderObject("shaders/bloom/blurVertex.glsl", "shaders/bloom/blurFragment.glsl");
	blur->activate();
		blur->setUniformi("scene", 0);
		blur->setUniformf("BlurWidth", 200.0);
		blur->setUniformfv("WindowSize",winSize, 2);
		blur->setUniformfv("weight",weight,7);
	blur->deactivate();

	composition = new ShaderObject ("shaders/bloom/compFragment.glsl");
	composition->activate();
		composition->setUniformi("scene", 0);
		composition->setUniformi("blurredScene",1);
		composition->setUniformf("SceneIntensity",0.5f);
		composition->setUniformf("GlowIntensity",0.5f);
		composition->setUniformf("HighlightIntensity",0.3f);
	composition->deactivate();
}

cBloom::~cBloom()
{
	FREE_MEMORY(sceneFBO);
	FREE_MEMORY(displayTexRect);
	FREE_MEMORY(downSampleFBO);
	FREE_MEMORY(downSampleShader);
	FREE_MEMORY(blur);
	FREE_MEMORY(rb);
	FREE_MEMORY(composition);

	FREE_TEXTURE(sceneTexId);
	FREE_TEXTURE(downSampleTexId);
	FREE_TEXTURE(blurHTexId);
	FREE_TEXTURE(finalBlurTexId);
	FREE_TEXTURE(finalCompTexId);
	
}

void cBloom::postProcess()
{
	float direction[2] = {1.0,0.0};

	// downsampling the main view
	downSampleFBO->Bind();
		glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
		glClear(GL_COLOR_BUFFER_BIT);
		setFBOProjection(wDown,hDown);
			glActiveTexture(GL_TEXTURE0);
			glBindTexture(texTarget, sceneTexId);
			downSampleShader->activate();
				DrawQuad(wDown, hDown, w, h, texTarget);
			downSampleShader->deactivate();
			glBindTexture(texTarget, 0);
	
			//performing horizontal blur
			glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
			//glClearColor(1.0,0.0,0.0,1.0);
			glClearColor(0.0,0.0,0.0,0.0);
			glClear(GL_COLOR_BUFFER_BIT);
			glBindTexture(texTarget, downSampleTexId);
			blur->activate();
				blur->setUniformfv("direction", direction, 2);
				DrawQuad(wDown, hDown, wDown, hDown, texTarget);
			blur->deactivate();
			glBindTexture(texTarget, 0);

			//performing vertical blur
			glDrawBuffer(GL_COLOR_ATTACHMENT2_EXT);
			//glClearColor(1.0,0.0,0.0,1.0);
			glClearColor(0.0,0.0,0.0,0.0);
			glClear(GL_COLOR_BUFFER_BIT);
			glBindTexture(texTarget, blurHTexId);
			blur->activate();
				direction[0] = 0.0;
				direction[1] = 1.0;
				blur->setUniformfv("direction", direction, 2);
				DrawQuad(wDown, hDown, wDown, hDown, texTarget);
			blur->deactivate();
			
		glBindTexture(texTarget, 0);
		restoreProjection();
	downSampleFBO->Disable();

	sceneFBO->Bind();
		glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
		glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		setFBOProjection(w,h);
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(texTarget, sceneTexId);
		glActiveTexture(GL_TEXTURE1);
		glBindTexture(texTarget, finalBlurTexId);
		composition->activate();
			DrawQuad(w,h,w,h, texTarget);
		composition->deactivate();
		glBindTexture(texTarget, 0);
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(texTarget, 0);
		restoreProjection();
	sceneFBO->Disable();
	//DisplayTexture();

}

void cBloom::DrawQuad (int w, int h, int tw, int th, int texTarget)
{
	if (texTarget == GL_TEXTURE_RECTANGLE)
	{
		glBegin(GL_QUADS);
			glTexCoord2f(0,         0);         glVertex2f(0,        0);
			glTexCoord2f((float)tw, 0);         glVertex2f((float)w, 0);
			glTexCoord2f((float)tw, (float)th); glVertex2f((float)w, (float) h);
			glTexCoord2f(0,         (float)th); glVertex2f(0,        (float) h);
		glEnd();
	}
	else if (texTarget == GL_TEXTURE_2D_ARRAY_EXT)
	{
		glBegin(GL_QUADS);
			glTexCoord3f(0.0, 0.0, 0.0); glVertex2f(0,        0);
			glTexCoord3f(1.0, 0.0, 0.0); glVertex2f((float)w, 0);
			glTexCoord3f(1.0, 1.0, 0.0); glVertex2f((float)w, (float) h);
			glTexCoord3f(0.0, 1.0, 0.0); glVertex2f(0,        (float) h);
		glEnd();
	}
	else if (texTarget == GL_TEXTURE_2D)
	{
		glBegin(GL_QUADS);
			glTexCoord2f(0.0, 0.0); glVertex2f(0.0, 0.0);
			glTexCoord2f(1.0, 0.0); glVertex2f((float)w, 0.0);
			glTexCoord2f(1.0, 1.0); glVertex2f((float)w, (float) h);
			glTexCoord2f(0.0, 1.0); glVertex2f(0.0,        (float) h);
		glEnd();
	}
}


void cBloom::DisplayTexture ()
{
	GLuint texId = finalCompTexId;
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    //glOrtho(0.0, wDown, 0.0, hDown, -1.0, 1.0);
	glOrtho(0.0, w, 0.0, h, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();
	
	
	//glScalef(0.5f, 0.5f, 1.0f);	 

	if (texTarget == GL_TEXTURE_RECTANGLE)
	{
		glActiveTexture (GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_RECTANGLE, texId);
		displayTexRect->activate ();
			//DrawQuad(wDown,hDown, wDown, hDown, texTarget);
			DrawQuad(w,h, w,h, texTarget);
		displayTexRect->deactivate ();
		glBindTexture(GL_TEXTURE_RECTANGLE, 0);
	}
	else if (texTarget == GL_TEXTURE_2D)
	{
		glBindTexture(GL_TEXTURE_2D, texId);
		displayTex2D->activate ();
			DrawQuad(w, h, 1, 1, texTarget);
		displayTex2D->deactivate ();
		glBindTexture(GL_TEXTURE_2D, 0);

	}
	else if (texTarget == GL_TEXTURE_2D_ARRAY_EXT)
	{
		glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, texId);
		displayTexRect->activate ();
			DrawQuad(w, h, w, h, texTarget);
		displayTexRect->deactivate ();
		glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, 0);

	}
	glPopMatrix();

    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
}

void cBloom::setFBOProjection (int w, int h)
{
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	glGetIntegerv(GL_VIEWPORT, vp);
	glViewport (0,0, w, h);
	glOrtho(0.0, w, 0.0, h, -1,1);
	glMatrixMode (GL_MODELVIEW);
	glPushMatrix ();
	glLoadIdentity ();
}

void cBloom::restoreProjection ()
{
	glPopMatrix ();
	glMatrixMode(GL_PROJECTION);
	glViewport (vp[0],vp[1], vp[2], vp[3]);
	glPopMatrix();
	glMatrixMode (GL_MODELVIEW);
}

